﻿using System;
using System.Collections.Generic;
using HMEngine.HMEffects;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace HMEngine.HMComponents {
    internal sealed class HMComponentException : Exception {
        internal HMComponentException(string s) : base(s) { }
    }

    internal sealed class HMComponentManager : DrawableGameComponent {
        private static readonly Dictionary<string, IHMComponent> masterList = new Dictionary<string, IHMComponent>();
        private static readonly Dictionary<string, IHMInitializable> initializable = new Dictionary<string, IHMInitializable>();
        private static readonly Dictionary<string, IHMLoadable> loadable = new Dictionary<string, IHMLoadable>();
        private static readonly Dictionary<string, IHMUpdatable> updatable = new Dictionary<string, IHMUpdatable>();
        private static readonly Dictionary<string, IHMRenderable> renderable = new Dictionary<string, IHMRenderable>();
        private static readonly Dictionary<string, IDisposable> disposable = new Dictionary<string, IDisposable>();

        internal HMComponentManager(Game game) : base(game) { }

        public static void AddComponent(string name, IHMComponent component) {
            masterList.Add(name, component);

            if (component is IHMInitializable) {
                initializable.Add(name, (IHMInitializable)component);
            }

            if (component is IHMLoadable) {
                loadable.Add(name, (IHMLoadable)component);
            }

            if (component is IHMUpdatable) {
                updatable.Add(name, (IHMUpdatable)component);
            }

            if (component is IHMRenderable) {
                renderable.Add(name, (IHMRenderable)component);
            }

            if (component is IDisposable) {
                disposable.Add(name, (IDisposable)component);
            }
        }

        internal static T GetComponent<T>(string name) {
            if (masterList.ContainsKey(name) && masterList[name] is T) {
                return (T)masterList[name];
            }

            throw new HMComponentException("No component with the name " + name + " exists");
        }

        public override void Initialize() {
            foreach (IHMInitializable initialize in initializable.Values) {
                initialize.Initialize();
            }

            base.Initialize();
        }

        protected override void LoadContent() {
            foreach (IHMLoadable load in loadable.Values) {
                load.LoadContent(GraphicsDevice, Game.Content);
            }

            base.LoadContent();
        }

        public override void Update(GameTime gameTime) {
            foreach (IHMUpdatable update in updatable.Values) {
                update.Update(gameTime);
            }

            base.Update(gameTime);
        }

        public override void Draw(GameTime gameTime) {
            foreach (IHMRenderable render in renderable.Values) {
                HMEffectManager.SetActiveShader(render.Shader);
                HMEffectManager.ActiveShader.SetParameters(render);

                foreach (EffectPass pass in HMEffectManager.ActiveShader.Effect.CurrentTechnique.Passes) {
                    pass.Apply();
                    render.Render(GraphicsDevice);
                }
            }

            base.Draw(gameTime);
        }

        protected override void UnloadContent() {
            foreach (IHMLoadable unloadcontent in loadable.Values) {
                unloadcontent.UnloadContent();
            }

            base.UnloadContent();
        }

        protected override void Dispose(bool disposing) {
            foreach (IDisposable dispose in disposable.Values) {
                dispose.Dispose();
            }

            base.Dispose(disposing);
        }
    }
}